﻿using System;
using System.IO;
using System.IO.Ports;
using System.Threading.Tasks;

namespace YWM.Communication
{
    public class SerialPortUtil : CommunicationUtil
    {
        /// <summary>
        /// 串口类
        /// </summary>
        private SerialPort _serialPort;

        /// <summary>
        /// 串口是否打开
        /// </summary>
        public override bool Connected
        {
            get
            {
                return _serialPort != null && _serialPort.IsOpen;
            }
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <param name="portName">串口名</param>
        /// <param name="baudrate">波特率</param>
        /// <returns></returns>
        public override (bool, Exception) Connect(string portName, int baudrate)
        {
            try
            {
                _serialPort = new SerialPort();
                _serialPort.ReadTimeout = SendTimeout;
                _serialPort.WriteTimeout = ReceiveTimeout;
                _serialPort.PortName = portName;
                _serialPort.BaudRate = baudrate;
                _serialPort.Parity = Parity.None;
                _serialPort.DataBits = 8;
                _serialPort.StopBits = StopBits.One;
                _serialPort.Open();
                return (true, null);
            }
            catch (Exception ex)
            {
                return (false, ex);
            }
        }

        /// <summary>
        /// 异步实现串口写入并读取
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public override async Task<(byte[], Exception)> SendAndReceiveAsync(byte[] buffer)
        {
            using (await _mutex.LockAsync())
            {
                (byte[], Exception) tuple = await Task.Factory.StartNew(state =>
                {
                    byte[] data = null;
                    Exception exception = null;
                    try
                    {
                        byte[] writeBuffer = (byte[])state;
                        _serialPort.Write(writeBuffer, 0, writeBuffer.Length);
                        int receiveTime = 0;
                        do
                        {
                            Task.Delay(Delay).Wait();
                            byte[] readBuffer = new byte[_serialPort.BytesToRead];
                            int count = _serialPort.Read(readBuffer, 0, readBuffer.Length);
                            if (count > 0)
                            {
                                MemoryStream stream = new MemoryStream();
                                stream.Write(readBuffer, 0, count);
                                data = stream.ToArray();
                                break;
                            }
                            receiveTime += Delay;
                        } while (ReceiveTimeout > receiveTime);
                    }
                    catch (Exception ex)
                    {
                        exception = ex;
                    }
                    return (data, exception);
                }, buffer);
                return tuple;
            }
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public override void Disconnect()
        {
            if (Connected)
            {
                _serialPort.Close();
            }
        }
    }
}
